//-----------------------------------------------------------------------
// <copyright file="AzManTransportHelper.cs" company="Alan Rodrigues">
//     Copyright (c) Alan Rodrigues. All rights reserved.
// </copyright>
// <developed>Alan Rodrigues (operninha@gmail.com)</developed>
//-----------------------------------------------------------------------

//#region License Info

//*
//Copyright 2006 Joe Langley (EE 2006)

//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

//--------------------------------

//You must not remove this License Info from your applications! 
//Please send all questions or updates to: joe_langley78@hotmail.com

//Joe Langley
//joe_langley78@hotmail.com

//LAST UPDATE: 9/05/2006

//EE 2006
//*/

//#endregion

using System;
using System.Collections;
//using System.DirectoryServices;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.Interop.Security.AzRoles;
using Authorization;
using System.Collections.Generic;
////use interop version created by Microsoft
////v. 1.2.0.0 used now after SP1 install

namespace AuthorizationContextHelper
{
    /// <summary>
    ///	NOTE I HAVE LEFT IN TRY/CATCH JUST SO WE KNOW DOWN THE ROAD IN CASE ERROR HAPPENS
    /// </summary>
    public class AzManTransportHelper
    {
        #region Consts

        private const int AZ_AZSTORE_FLAG_AUDIT_IS_CRITICAL = 8;
        private const int AZ_AZSTORE_FLAG_BATCH_UPDATE = 4;
        private const int AZ_AZSTORE_FLAG_CREATE = 1;
        private const int AZ_AZSTORE_FLAG_MANAGE_STORE_ONLY = 2;

        #endregion

        #region Constructors

        private AzManTransportHelper()
        {
        }

        #endregion

        #region Implementation - Public

        /// <summary>
        /// Copy store on url srcUrl to store on destUrl.
        /// </summary>
        /// <param name="srcUrl">Target store url.</param>
        /// <param name="destUrl">Destination store url</param>
        /// <param name="patchOnly">If True - only applications existing in both stores
        ///  updated; if False - destination store applications removed and new added.
        /// </param>
        public static void CopyStore(string srcUrl, string destUrl, IEnumerable appsNames, bool patchOnly)
        {
            if (srcUrl == null || srcUrl.Length == 0)
            {
                throw new ArgumentNullException("srcUrl", "Source Url can not be null or empty");
            }
            if (destUrl == null || destUrl.Length == 0)
            {
                throw new ArgumentNullException("destUrl", "Destination Url can not be null or empty");
            }

            IAzAuthorizationStore3 srcStore = new AzAuthorizationStoreClass();
            srcStore.Initialize(0, srcUrl, null);

            IAzAuthorizationStore3 destStore = CopyStore(destUrl, srcStore);

            UpdateApplicationGroups(srcStore.ApplicationGroups, destStore.ApplicationGroups,
                                    typeof(IAzAuthorizationStore), destStore, patchOnly);

            UpdateApplications(srcStore, ref destStore, appsNames, patchOnly);
        }

        public static void CopyStore(string srcUrl, AuthorizationStore destStore, IEnumerable appsNames, bool patchOnly)
        {
            if (srcUrl == null || srcUrl.Length == 0)
            {
                throw new ArgumentNullException("srcUrl", "Source Url can not be null or empty");
            }

            if (destStore == null)
            {
                throw new ArgumentNullException("destStore", "Source Store can not be null");
            }

            IAzAuthorizationStore3 srcStore = new AzAuthorizationStoreClass();
            srcStore.Initialize(0, srcUrl, null);

            IAzAuthorizationStore3 destStore2 = CopyStore(destStore.Store, srcStore);

            UpdateApplicationGroups(srcStore.ApplicationGroups, destStore2.ApplicationGroups,
                                    typeof(IAzAuthorizationStore), destStore, patchOnly);

            UpdateApplications(srcStore, ref destStore2, appsNames, patchOnly);
        }

        public static void CopyStore(IAzAuthorizationStore3 srcStore, string destUrl, IEnumerable appsNames, bool patchOnly)
        {
            if (srcStore == null)
            {
                throw new ArgumentNullException("srcStore", "Source Store can not be null");
            }
            if (destUrl == null || destUrl.Length == 0)
            {
                throw new ArgumentNullException("destUrl", "Destination Url can not be null or empty");
            }

            IAzAuthorizationStore3 destStore = CopyStore(destUrl, srcStore);

            UpdateApplicationGroups(srcStore.ApplicationGroups, destStore.ApplicationGroups,
                                    typeof(IAzAuthorizationStore), destStore, patchOnly);

            UpdateApplications(srcStore, ref destStore, appsNames, patchOnly);
        }

        public static void CopyApplications(IAzAuthorizationStore3 srcStore, string destUrl, IEnumerable appsNames, bool patchOnly, bool isCopyWithAdministrators, bool isCopyWithUsers)
        {
            if (srcStore == null)
            {
                throw new ArgumentNullException("srcStore", "Source Store can not be null");
            }
            if (destUrl == null || destUrl.Length == 0)
            {
                throw new ArgumentNullException("destUrl", "Destination Url can not be null or empty");
            }

            IAzAuthorizationStore3 destStore = CopyApplicationToStore(destUrl, srcStore, isCopyWithAdministrators, isCopyWithUsers);
            
            UpdateApplicationGroups(srcStore.ApplicationGroups, destStore.ApplicationGroups,
                                    typeof(IAzAuthorizationStore), destStore, patchOnly);

            UpdateApplications(srcStore, ref destStore, appsNames, patchOnly);
        }

        public static void CopyApplications(string srcUrl, AuthorizationStore destStore, IEnumerable appsNames, bool patchOnly)
        {
            if (srcUrl == null || srcUrl.Length == 0)
            {
                throw new ArgumentNullException("srcUrl", "Source Url can not be null or empty");
            }

            if (destStore == null)
            {
                throw new ArgumentNullException("destStore", "Source Store can not be null");
            }

            IAzAuthorizationStore3 srcStore = new AzAuthorizationStoreClass();
            srcStore.Initialize(0, srcUrl, null);

            IAzAuthorizationStore3 azDestStore = destStore.Store;

            UpdateApplicationGroups(srcStore.ApplicationGroups, destStore.Store.ApplicationGroups,
                                    typeof(IAzAuthorizationStore), azDestStore, patchOnly);

            UpdateApplications(srcStore, ref azDestStore, appsNames, patchOnly);
        }

        #endregion

        #region Implementation - OperationCollection

        /*private static void UpdateOperations(IAzOperations srcOperations, IAzOperations destOperations, Type objectInterface, object destObj)
        {
            ArrayList curOperations = new ArrayList(); 
            foreach(IAzOperation destOperation in destOperations)
            {
                curOperations.Add(destOperation.Name);
            }
            foreach(string operationName in curOperations)
            {
                MethodInfo delMethod = objectInterface.GetMethod("DeleteOperation");
                delMethod.Invoke(destObj, new object[]{operationName, null});
            }
            MethodInfo submitMethod = objectInterface.GetMethod("Submit");
            submitMethod.Invoke(destObj, new object[]{0, null});
            PropertyInfo operationsProp = objectInterface.GetProperty("OperationCollection");
            destOperations = (IAzOperations)operationsProp.GetValue(destObj, null);
            foreach(IAzOperation srcOperation in srcOperations)
            {
                if (curOperations.Contains(srcOperation.Name))
                {
                    curOperations.Remove(srcOperation.Name); 
                }
                UpdateOperation(objectInterface, destObj, destOperations, srcOperation);
            }
        }*/

        private static void UpdateOperations(IAzOperations srcOperations, IAzOperations destOperations,
                                             Type objectInterface, object destObj)
        {
            ArrayList curOperations = new ArrayList();

            foreach (IAzOperation destOperation in destOperations)
            {
                curOperations.Add(destOperation.Name);
            }

            foreach (IAzOperation srcOperation in srcOperations)
            {
                if (curOperations.Contains(srcOperation.Name))
                {
                    curOperations.Remove(srcOperation.Name);
                }
                UpdateOperation(objectInterface, destObj, destOperations, srcOperation);
            }

            foreach (string operationName in curOperations)
            {
                MethodInfo addMethod = objectInterface.GetMethod("DeleteOperation");
                addMethod.Invoke(destObj, new object[] { operationName, null });
            }
        }

        private static IAzOperation UpdateOperation(Type objectInterface, object destObj, IAzOperations destOperations,
                                                    IAzOperation srcOperation)
        {
            IAzOperation destOperation = null;
            foreach (IAzOperation currentOperation in destOperations)
            {
                if (currentOperation.Name.Equals(srcOperation.Name))
                {
                    destOperation = currentOperation;
                    break;
                }
            }

            if (destOperation == null)
            {
                MethodInfo addMethod = objectInterface.GetMethod("CreateOperation");
                destOperation = (IAzOperation)addMethod.Invoke(destObj, new object[] { srcOperation.Name, null });
            }

            SetIfNotEqual(srcOperation, destOperation, typeof(IAzOperation), "ApplicationData");
            SetIfNotEqual(srcOperation, destOperation, typeof(IAzOperation), "Description");
            SetIfNotEqual(srcOperation, destOperation, typeof(IAzOperation), "OperationID");

            destOperation.Submit(0, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destOperation;
        }

        #endregion

        #region Implementation - Tasks

        private static void UpdateTasks(IAzTasks srcTasks, IAzTasks destTasks, Type objectInterface, object destObj)
        {
            ArrayList curTasks = new ArrayList();

            foreach (IAzTask2 destTask in destTasks)
            {
                curTasks.Add(destTask.Name);
            }

            foreach (IAzTask2 srcTask in srcTasks)
            {
                if (curTasks.Contains(srcTask.Name))
                {
                    curTasks.Remove(srcTask.Name);
                }
                UpdateTask(objectInterface, destObj, destTasks, srcTask);
            }

            foreach (string name in curTasks)
            {
                MethodInfo addMethod = objectInterface.GetMethod("DeleteTask");
                addMethod.Invoke(destObj, new object[] { name, null });
            }

            PropertyInfo tasksProperty = objectInterface.GetProperty("Tasks", typeof(IAzTasks));
            tasksProperty = objectInterface.GetProperty("Tasks");
            IAzTasks tasks = (IAzTasks)tasksProperty.GetValue(destObj, new object[] { });

            ApplyTaskChildren(srcTasks, tasks);
        }

        private static IAzTask2 UpdateTask(Type objectInterface, object destObj, IAzTasks destTasks, IAzTask2 srcTask)
        {
            IAzTask2 destTask = null;
            foreach (IAzTask2 currentTask in destTasks)
            {
                if (currentTask.Name.Equals(srcTask.Name))
                {
                    destTask = currentTask;
                    break;
                }
            }

            if (destTask == null)
            {
                MethodInfo addMethod = objectInterface.GetMethod("CreateTask");
                destTask = (IAzTask2)addMethod.Invoke(destObj, new object[] { srcTask.Name, null });
            }

            SetIfNotEqual(srcTask, destTask, typeof(IAzTask2), "ApplicationData");
            SetIfNotEqual(srcTask, destTask, typeof(IAzTask2), "BizRuleLanguage");
            SetIfNotEqual(srcTask, destTask, typeof(IAzTask2), "BizRule");
            SetIfNotEqual(srcTask, destTask, typeof(IAzTask2), "BizRuleImportedPath");
            SetIfNotEqual(srcTask, destTask, typeof(IAzTask2), "Description");
            SetIfNotEqual(srcTask, destTask, typeof(IAzTask2), "IsRoleDefinition");

            SynchronizeCollections(srcTask.Operations, destTask.Operations, typeof(IAzTask2), destTask, "AddOperation",
                                   "DeleteOperation");

            destTask.Submit(0, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destTask;
        }

        private static void ApplyTaskChildren(IAzTasks srcTasks, IAzTasks destTasks)
        {
            foreach (IAzTask2 srcTask in srcTasks)
            {
                IAzTask2 destTask = null;
                foreach (IAzTask2 currentTask in destTasks)
                {
                    if (currentTask.Name.Equals(srcTask.Name))
                    {
                        destTask = currentTask;
                        break;
                    }
                }

                if (destTask != null)
                {
                    SynchronizeCollections(srcTask.Tasks, destTask.Tasks, typeof(IAzTask2), destTask, "AddTask",
                                           "DeleteTask");
                    destTask.Submit(0, null);
                    AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();
                }
            }
        }

        #endregion

        #region Implementation - ScopeCollection

        private static void UpdateScopes(IAzScopes srcScopes, IAzScopes destScopes, Type objectInterface, object destObj)
        {
            ArrayList curScopes = new ArrayList();

            foreach (IAzScope destScope in destScopes)
            {
                curScopes.Add(destScope.Name);
            }

            foreach (IAzScope srcScope in srcScopes)
            {
                if (curScopes.Contains(srcScope.Name))
                {
                    curScopes.Remove(srcScope.Name);
                }

                UpdateScope(objectInterface, destObj, destScopes, srcScope);
            }

            foreach (string scopeName in curScopes)
            {
                MethodInfo addMethod = objectInterface.GetMethod("DeleteScope");
                addMethod.Invoke(destObj, new object[] { scopeName, null });
            }
        }

        private static IAzScope UpdateScope(Type objectInterface, object destObj, IAzScopes destScopes,
                                            IAzScope srcScope)
        {
            IAzScope destScope = null;
            foreach (IAzScope currentScope in destScopes)
            {
                if (currentScope.Name.Equals(srcScope.Name))
                {
                    destScope = currentScope;
                    break;
                }
            }

            if (destScope == null)
            {
                MethodInfo addMethod = objectInterface.GetMethod("CreateScope");
                destScope = (IAzScope)addMethod.Invoke(destObj, new object[] { srcScope.Name, null });
            }

            SetIfNotEqual(srcScope, destScope, typeof(IAzScope), "ApplicationData");
            SetIfNotEqual(srcScope, destScope, typeof(IAzScope), "Description");

            destScope.Submit(0, null);            

            UpdateApplicationGroups(srcScope.ApplicationGroups, destScope.ApplicationGroups, typeof(IAzScope),
                                    destScope, false);

            UpdateTasks(srcScope.Tasks, destScope.Tasks, typeof(IAzScope), destScope);

            UpdateRoles(srcScope.Roles, destScope.Roles, typeof(IAzScope), destScope);

            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destScope;
        }

        #endregion

        #region Implementation - Roles

        private static void UpdateRoles(IAzRoles srcRoles, IAzRoles destRoles, Type objectInterface, object destObj)
        {
            ArrayList curRoles = new ArrayList();

            foreach (IAzRole destRole in destRoles)
            {
                curRoles.Add(destRole.Name);
            }

            foreach (IAzRole srcRole in srcRoles)
            {
                if (curRoles.Contains(srcRole.Name))
                {
                    curRoles.Remove(srcRole.Name);
                }
                UpdateRole(objectInterface, destObj, destRoles, srcRole);
            }

            foreach (string roleName in curRoles)
            {
                MethodInfo addMethod = objectInterface.GetMethod("DeleteRole");
                addMethod.Invoke(destObj, new object[] { roleName, null });
            }
        }

        private static IAzRole UpdateRole(Type objectInterface, object destObj, IAzRoles destRoles, IAzRole srcRole)
        {
            if (objectInterface == null)
                throw new ArgumentNullException("objectInterface");
            if (destObj == null)
                throw new ArgumentNullException("destObj");
            if (destRoles == null)
                throw new ArgumentNullException("destRoles");
            if (srcRole == null)
                throw new ArgumentNullException("srcRole");
            IAzRole destRole = null;
            foreach (IAzRole currentRole in destRoles)
            {
                if (currentRole.Name.Equals(srcRole.Name))
                {
                    destRole = currentRole;
                    break;
                }
            }
            if (destRole == null)
            {
                MethodInfo addMethod = objectInterface.GetMethod("CreateRole");
                destRole = (IAzRole)addMethod.Invoke(destObj, new object[] { srcRole.Name, null });
            }
            if (destRole == null)
                throw new NullReferenceException("destRole is null");
            SetIfNotEqual(srcRole, destRole, typeof(IAzRole), "ApplicationData");
            SetIfNotEqual(srcRole, destRole, typeof(IAzRole), "Description");
            try
            {
                SynchronizeCollections(srcRole.Members, destRole.Members, typeof(IAzRole), destRole, "AddMember",
                                       "DeleteMember");
            }
            catch (Exception innerException)
            {
                throw new Exception("failure on Members", innerException);
            }
            try
            {
                SynchronizeCollections(srcRole.AppMembers, destRole.AppMembers, typeof(IAzRole), destRole,
                                       "AddAppMember", "DeleteAppMember");
            }
            catch (Exception innerException)
            {
                throw new Exception("failure on Members", innerException);
            }
            try
            {
                SynchronizeCollections(srcRole.Operations, destRole.Operations, typeof(IAzRole), destRole,
                                       "AddOperation", "DeleteOperation");
            }
            catch (Exception innerException)
            {
                throw new Exception("failure on OperationCollection", innerException);
            }
            try
            {
                SynchronizeCollections(srcRole.Tasks, destRole.Tasks, typeof(IAzRole), destRole, "AddTask",
                                       "DeleteTask");
            }
            catch (Exception innerException)
            {
                throw new Exception("failure on Tasks", innerException);
            }

            try
            {
                destRole.Submit(0, null);
                AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();
            }
            catch (Exception innerException)
            {
                throw new Exception("failure on Submit", innerException);
            }
            return destRole;
        }

        #endregion

        #region Implementation - ApplicationCollection

        private static void UpdateApplications(IAzAuthorizationStore3 srcStore, ref IAzAuthorizationStore3 destStore, IEnumerable appsNames, bool patchOnly)
        {
            List<string> tobeCopied = new List<string>();

            foreach (string appName in appsNames)
            {
                tobeCopied.Add(appName);
            }

            ArrayList curApplications = new ArrayList();

            foreach (IAzApplication destApplication in destStore.Applications)
            {
                curApplications.Add(destApplication.Name);
            }
            foreach (IAzApplication srcApplication in srcStore.Applications)
            {
                if (tobeCopied.Contains(srcApplication.Name) == false)
                {
                    continue;
                }
                if (curApplications.Contains(srcApplication.Name))
                {
                    curApplications.Remove(srcApplication.Name);
                }
                // Remove applicationName and reopen store
                foreach (IAzApplication currentApplication in destStore.Applications)
                {
                    if (currentApplication.Name.Equals(srcApplication.Name))
                    {
                        destStore.DeleteApplication(srcApplication.Name, null);
                        // We have to reopen the store due to the bug in AzMan implementation.
                        // At least for now I have not found a way to avoid this.
                        destStore.Submit(0, null);
                        destStore.UpdateCache(null);
                        AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();
                        //destStore = OpenStore(destUrl);
                        break;
                    }
                }
                UpdateApplication(destStore, srcApplication);
            }
            if (patchOnly == false)
            {
                foreach (string applicationName in curApplications)
                {
                    destStore.DeleteApplication(applicationName, null);
                }
            }
        }

        private static IAzApplication UpdateApplication(IAzAuthorizationStore destStore, IAzApplication srcApplication)
        {
            IAzApplication destApplication = null;
            foreach (IAzApplication currentApplication in destStore.Applications)
            {
                if (currentApplication.Name.Equals(srcApplication.Name))
                {
                    destApplication = currentApplication;
                    break;
                }
            }

            if (destApplication == null)
            {
                //destStore.CreateApplication(srcApplication.Name, , version, description, applicationData);
                //destApplication = AuthorizationStoreManager.CreateApplication(srcApplication.
                destApplication = destStore.CreateApplication(srcApplication.Name, null);
            }

            SetIfNotEqual(srcApplication, destApplication, typeof(IAzApplication), "ApplicationData");
            SetIfNotEqual(srcApplication, destApplication, typeof(IAzApplication), "AuthzInterfaceClsid");
            SetIfNotEqual(srcApplication, destApplication, typeof(IAzApplication), "Description");
            //SetIfNotEqual(srcApplication, destApplication, typeof(IAzApplication), "ApplyStoreSacl");
            //SetIfNotEqual(srcApplication, destApplication, typeof(IAzApplication), "GenerateAudits");

            destApplication.Submit(0, null);

            UpdateApplicationGroups(srcApplication.ApplicationGroups, destApplication.ApplicationGroups,
                                    typeof(IAzApplication), destApplication, false);

            UpdateOperations(srcApplication.Operations, destApplication.Operations, typeof(IAzApplication),
                             destApplication);

            UpdateTasks(srcApplication.Tasks, destApplication.Tasks, typeof(IAzApplication), destApplication);

            UpdateRoles(srcApplication.Roles, destApplication.Roles, typeof(IAzApplication), destApplication);

            UpdateScopes(srcApplication.Scopes, destApplication.Scopes, typeof(IAzApplication), destApplication);

            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destApplication;
        }

        #endregion

        #region Implementation - Application Groups

        private static void UpdateApplicationGroups(IAzApplicationGroups srcGrpoups, IAzApplicationGroups destGroups,
                                                    Type objectInterface, object destObj, bool patchOnly)
        {
            ArrayList curApplicationGroups = new ArrayList();

            foreach (IAzApplicationGroup2 destApplicationGroup in destGroups)
            {
                curApplicationGroups.Add(destApplicationGroup.Name);
            }

            foreach (IAzApplicationGroup2 srcApplicationGroup in srcGrpoups)
            {
                if (curApplicationGroups.Contains(srcApplicationGroup.Name))
                {
                    curApplicationGroups.Remove(srcApplicationGroup.Name);
                }
                UpdateApplicationGroup(objectInterface, destObj, destGroups, srcApplicationGroup);
            }

            if (patchOnly == false)
            {
                foreach (string groupName in curApplicationGroups)
                {
                    MethodInfo addMethod = objectInterface.GetMethod("DeleteApplicationGroup");
                    addMethod.Invoke(destObj, new object[] { groupName, null });
                }
            }

            PropertyInfo applicationGroupsProperty =
                objectInterface.GetProperty("ApplicationGroups", typeof(IAzApplicationGroups));
            applicationGroupsProperty = objectInterface.GetProperty("ApplicationGroups");
            IAzApplicationGroups groups = (IAzApplicationGroups)
                                          applicationGroupsProperty.GetValue(destObj, new object[] { });

            ApplyApplicationGroupChildren(srcGrpoups, groups);
        }

        private static IAzApplicationGroup2 UpdateApplicationGroup(Type objectInterface, object destObj,
                                                                  IAzApplicationGroups destGroups,
                                                                  IAzApplicationGroup2 srcApplicationGroup)
        {
            IAzApplicationGroup2 destApplicationGroup = null;
            foreach (IAzApplicationGroup2 currentApplicationGroup in destGroups)
            {
                if (currentApplicationGroup.Name.Equals(srcApplicationGroup.Name))
                {
                    destApplicationGroup = currentApplicationGroup;
                    break;
                }
            }

            if (destApplicationGroup == null)
            {
                MethodInfo addMethod = objectInterface.GetMethod("CreateApplicationGroup");
                destApplicationGroup =
                    (IAzApplicationGroup2)addMethod.Invoke(destObj, new object[] { srcApplicationGroup.Name, null });
            }

            SetIfNotEqual(srcApplicationGroup, destApplicationGroup, typeof(IAzApplicationGroup2), "Description");
            SetIfNotEqual(srcApplicationGroup, destApplicationGroup, typeof(IAzApplicationGroup2), "LdapQuery");
            SetIfNotEqual(srcApplicationGroup, destApplicationGroup, typeof(IAzApplicationGroup2), "Type");

            SynchronizeCollections(
                srcApplicationGroup.Members,
                destApplicationGroup.Members,
                typeof(IAzApplicationGroup), destApplicationGroup, "AddMember", "DeleteMember"
                );

            SynchronizeCollections(
                srcApplicationGroup.NonMembers,
                destApplicationGroup.NonMembers,
                typeof(IAzApplicationGroup), destApplicationGroup, "AddNonMember", "DeleteNonMember"
                );

            destApplicationGroup.Submit(0, null);

            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destApplicationGroup;
        }

        private static void ApplyApplicationGroupChildren(IAzApplicationGroups srcGroups,
                                                          IAzApplicationGroups destGroups)
        {
            foreach (IAzApplicationGroup srcApplicationGroup in srcGroups)
            {
                IAzApplicationGroup destApplicationGroup = null;
                foreach (IAzApplicationGroup currentApplicationGroup in destGroups)
                {
                    if (currentApplicationGroup.Name.Equals(srcApplicationGroup.Name))
                    {
                        destApplicationGroup = currentApplicationGroup;
                        break;
                    }
                }

                if (destApplicationGroup != null)
                {
                    SynchronizeCollections(
                        srcApplicationGroup.AppMembers,
                        destApplicationGroup.AppMembers,
                        typeof(IAzApplicationGroup2), destApplicationGroup, "AddAppMember", "DeleteAppMember"
                        );

                    SynchronizeCollections(
                        srcApplicationGroup.AppNonMembers,
                        destApplicationGroup.AppNonMembers,
                        typeof(IAzApplicationGroup2), destApplicationGroup, "AddAppNonMember", "DeleteAppNonMember"
                        );

                    destApplicationGroup.Submit(0, null);
                }
            }
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();
        }

        private static IAzAuthorizationStore3 CopyApplicationToStore(string destUrl, IAzAuthorizationStore srcStore, bool isCopyWithAdministrators, bool isCopyWithUsers)
        {
            IAzAuthorizationStore3 destStore = new AzAuthorizationStoreClass();

            try
            {
                destStore.Initialize(AZ_AZSTORE_FLAG_BATCH_UPDATE, destUrl, null);
            }
            catch
            {
                try
                {
                    destStore.Initialize(1, destUrl, null);
                }
                catch
                {
                    destStore.Initialize(2, destUrl, null);
                }
            }

            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplicationData");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "Description");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "DomainTimeout");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "MaxScriptEngines");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplyStoreSacl");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "GenerateAudits");

            if (isCopyWithAdministrators)
            {
                //Policy administrators
                SynchronizeCollections(
                    (ICollection)srcStore.PolicyAdministrators,
                    (ICollection)destStore.PolicyAdministrators,
                    destStore.GetType(), destStore, "AddPolicyAdministrator", "DeletePolicyAdministrator"
                    );

            }

            if (isCopyWithUsers)
            {
                //Policy readers.
                SynchronizeCollections(
                    (ICollection)srcStore.PolicyReaders,
                    (ICollection)destStore.PolicyReaders,
                    destStore.GetType(), destStore, "AddPolicyReader", "DeletePolicyReader"
                    );

            }
            destStore.Submit(0, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destStore;
        }

        #endregion

        #region Implementation - Store

        private static IAzAuthorizationStore3 CopyStore(string destUrl, IAzAuthorizationStore srcStore)
        {
            IAzAuthorizationStore3 destStore = new AzAuthorizationStoreClass();

            //if (IsStoreExists(destUrl))
            //{
            try
            {
                destStore.Initialize(AZ_AZSTORE_FLAG_BATCH_UPDATE, destUrl, null);
            }
            catch
            {
                try
                {
                    destStore.Initialize(1, destUrl, null);
                }
                catch
                {
                    destStore.Initialize(2, destUrl, null);
                }
            }
            //	}
            //	else
            //	{
            //		destStore.Initialize(1, destUrl, null);
            //	}

            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplicationData");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "Description");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "DomainTimeout");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "MaxScriptEngines");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplyStoreSacl");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "GenerateAudits");

            //Policy administrators
            SynchronizeCollections(
                (ICollection)srcStore.PolicyAdministrators,
                (ICollection)destStore.PolicyAdministrators,
                destStore.GetType(), destStore, "AddPolicyAdministrator", "DeletePolicyAdministrator"
                );

            //Policy readers.
            SynchronizeCollections(
                (ICollection)srcStore.PolicyReaders,
                (ICollection)destStore.PolicyReaders,
                destStore.GetType(), destStore, "AddPolicyReader", "DeletePolicyReader"
                );

            destStore.Submit(0, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destStore;
        }

        private static IAzAuthorizationStore3 CopyStore(IAzAuthorizationStore3 destStore, string srcUrl)
        {
            IAzAuthorizationStore3 srcStore = new AzAuthorizationStoreClass();

            //if (IsStoreExists(destUrl))
            //{
            try
            {
                srcStore.Initialize(AZ_AZSTORE_FLAG_BATCH_UPDATE, srcUrl, null);
            }
            catch
            {
                srcStore.Initialize(1, srcUrl, null);
            }
            //	}
            //	else
            //	{
            //		destStore.Initialize(1, destUrl, null);
            //	}

            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplicationData");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "Description");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "DomainTimeout");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "MaxScriptEngines");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplyStoreSacl");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "GenerateAudits");

            //Policy administrators
            SynchronizeCollections(
                (ICollection)srcStore.PolicyAdministrators,
                (ICollection)destStore.PolicyAdministrators,
                destStore.GetType(), destStore, "AddPolicyAdministrator", "DeletePolicyAdministrator"
                );

            //Policy readers.
            SynchronizeCollections(
                (ICollection)srcStore.PolicyReaders,
                (ICollection)destStore.PolicyReaders,
                destStore.GetType(), destStore, "AddPolicyReader", "DeletePolicyReader"
                );

            destStore.Submit(0, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destStore;
        }

        private static IAzAuthorizationStore3 CopyStore(IAzAuthorizationStore3 destStore, IAzAuthorizationStore srcStore)
        {
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplicationData");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "Description");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "DomainTimeout");
            SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "MaxScriptEngines");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "ApplyStoreSacl");
            //SetIfNotEqual(srcStore, destStore, typeof(IAzAuthorizationStore), "GenerateAudits");

            //Policy administrators
            SynchronizeCollections(
                (ICollection)srcStore.PolicyAdministrators,
                (ICollection)destStore.PolicyAdministrators,
                destStore.GetType(), destStore, "AddPolicyAdministrator", "DeletePolicyAdministrator"
                );

            //Policy readers.
            SynchronizeCollections(
                (ICollection)srcStore.PolicyReaders,
                (ICollection)destStore.PolicyReaders,
                destStore.GetType(), destStore, "AddPolicyReader", "DeletePolicyReader"
                );

            destStore.Submit(0, null);
            AuthorizationSynchronizeManager.SynchronizeManager().UpdateCache();

            return destStore;
        }

        ///// <summary>
        ///// Return true if store exists.		
        ///// </summary>
        ///// <param name="storeUrl">Store URL.</param>
        ///// <returns>True if store exists</returns>
        //public static bool IsStoreExists(string storeUrl)
        //{
        //    string storeType = string.Empty;
        //    string pathToStore = storeUrl;
        //    int i = pathToStore.IndexOf("//") + 2;
        //    if (i < pathToStore.Length)
        //    {
        //        storeType = pathToStore.Substring(0, i);
        //        pathToStore = pathToStore.Substring(i);
        //    }

        //    switch (storeType)
        //    {
        //        case "msldap://":
        //            return IsLdapObjectExists(pathToStore);

        //        case "msxml://":
        //            return File.Exists(pathToStore);

        //        default:
        //            throw new ArgumentException(
        //                "Store URL must be in form 'msxml://<PathToTheFile>' or 'msldap://<CN=StoreName,CN=ProgramName,DC=DomainName,DC=com>'.",
        //                "storeUrl");
        //    }
        //}

        //private static bool IsLdapObjectExists(string ldapURL)
        //{
        //    if (ldapURL.StartsWith("LDAP"))
        //    {
        //        throw new ArgumentException("ldapURL url must be without prefix LDAP://", "ldapURL");
        //    }
        //    string[] urlParts = ldapURL.Split(',');
        //    StringBuilder rootUrl = new StringBuilder();
        //    StringBuilder filterUrl = new StringBuilder();
        //    foreach (string urlPart in urlParts)
        //    {
        //        if (!urlPart.StartsWith("CN"))
        //        {
        //            rootUrl.AppendFormat(",{0}", urlPart);
        //        }
        //        else
        //        {
        //            filterUrl.AppendFormat(",{0}", urlPart);
        //        }
        //    }

        //    if (rootUrl.Length > 0)
        //    {
        //        rootUrl.Remove(0, 1);
        //    }

        //    rootUrl.Insert(0, "LDAP://");

        //    Console.WriteLine(rootUrl);

        //    if (filterUrl.Length > 0)
        //    {
        //        filterUrl.Remove(0, 1);
        //    }

        //    Console.WriteLine(filterUrl);

        //    DirectoryEntry entry = new DirectoryEntry(rootUrl.ToString());
        //    DirectorySearcher searcher = new DirectorySearcher(entry);
        //    searcher.Filter = filterUrl.ToString();
        //    return (searcher.FindAll().Count > 0);
        //}

        #endregion

        #region Implementation - Utils

        private static void SynchronizeCollections(object srcCollection, object destCollection, Type objectInterface,
                                                   object destObj, string addMethodName, string removeMethodName)
        {
            if (srcCollection == null)
                throw new ArgumentNullException("srcCollection");
            if (destCollection == null)
                throw new ArgumentNullException("destCollection");
            if (addMethodName == null)
                throw new ArgumentNullException("addMethodName");
            if (removeMethodName == null)
                throw new ArgumentNullException("removeMethodName");

            ArrayList destItems = new ArrayList((ICollection)destCollection);
            if (destItems == null)
                throw new NullReferenceException("destItems is null");
            if (((ICollection)srcCollection) == null)
                throw new NullReferenceException("srcCollection is null");
            foreach (string item in (ICollection)srcCollection)
            {
                if (!destItems.Contains(item))
                {
                    MethodInfo addMethod = objectInterface.GetMethod(addMethodName);
                    if (addMethod == null)
                        throw new NullReferenceException("addMethod is null");
                    addMethod.Invoke(destObj, new object[] { item, null });
                }
                else
                {
                    destItems.Remove(item);
                }
            }
            foreach (string item in destItems)
            {
                MethodInfo removeMethod = objectInterface.GetMethod(removeMethodName);
                if (removeMethod == null)
                    throw new NullReferenceException("removeMethod is null");
                removeMethod.Invoke(destObj, new object[] { item, null });
            }
        }

        private static void SetIfNotEqual(object srcObject, object destObject, Type objectInterface, string propertyName)
        {
            PropertyInfo property = objectInterface.GetProperty(propertyName);
            object val = property.GetValue(srcObject, null);
            if (!val.Equals(property.GetValue(destObject, null)))
            {
                property.SetValue(destObject, val, null);
            }
        }

        #endregion

        #region Implementation - Store

        private static IAzAuthorizationStore OpenStore(string sUrl)
        {
            IAzAuthorizationStore store = new AzAuthorizationStoreClass();

            try
            {
                store.Initialize(AZ_AZSTORE_FLAG_BATCH_UPDATE, sUrl, null);
            }
            catch
            {
                store.Initialize(1, sUrl, null);
            }

            return store;
        }

        #endregion

        public static IEnumerable GetApplicationsNames(string srcUrl)
        {
            if (srcUrl == null || srcUrl.Length == 0)
            {
                throw new ArgumentNullException("srcUrl", "Source Url can not be null or empty");
            }

            IAzAuthorizationStore srcStore = new AzAuthorizationStoreClass();
            srcStore.Initialize(0, srcUrl, null);

            ArrayList applications = new ArrayList();
            foreach (IAzApplication srcApplication in srcStore.Applications)
            {
                applications.Add(srcApplication.Name);
            }
            return applications;
        }
    }
}